package com.xuecheng.search.service.impl;

import com.xuecheng.api.search.model.dto.CoursePubIndexDTO;
import com.xuecheng.api.search.model.qo.QueryCoursePubModel;
import com.xuecheng.common.domain.code.CommonErrorCode;
import com.xuecheng.common.domain.page.PageRequestParams;
import com.xuecheng.common.domain.page.PageVO;
import com.xuecheng.common.domain.response.RestResponse;
import com.xuecheng.common.exception.ExceptionCast;
import com.xuecheng.common.util.JsonUtil;
import com.xuecheng.common.util.StringUtil;
import com.xuecheng.search.common.constant.ContentSearchErrorCode;
import com.xuecheng.search.service.CoursePubSearchService;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Map;

/**
 * 课程搜索服务实现层(es原始Api实现)
 */
@Service
@Slf4j
public class CoursePubSearchServiceImpl implements CoursePubSearchService {
    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Value("${xuecheng.elasticsearch.course.index}")
    private String indexName;

    /**
     * 课程搜索
     *
     * @param pageParams 分页参数
     * @param queryModel 条件查询
     * @return 分页查询结果对象
     * ===业务分析===
     * 1.构建请求对象
     * 2.根据请求对象的数据,通过client来获得响应数据
     * 3.根据响应的数据后的结果封装pageVO返回
     */
    @Override
    public PageVO<CoursePubIndexDTO> coursePubIndexByCondition(PageRequestParams pageParams, QueryCoursePubModel queryModel) {
        PageVO<CoursePubIndexDTO> pageVO = null;
        Long pageNo = pageParams.getPageNo() == null ? PageRequestParams.DEFAULT_PAGE_NUM : pageParams.getPageNo();

        Integer pageSize = pageParams.getPageSize() == null ? PageRequestParams.DEFAULT_PAGE_SIZE : pageParams.getPageSize();
        try {

            //1.构建请求对象
            SearchRequest request = getRequest(pageParams, queryModel);

            //2.根据请求对象的数据,通过client获取响应数据
            SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);

            //3.根据响应的数据后的结果封装pageVO返回
            pageVO = getResponseMsg(response);

            pageVO.setPage((pageNo).intValue());
            pageVO.setPageSize((pageSize));


        } catch (IOException e) {
            log.error("获取课程索引数据失败:{}", e.getMessage());
            ExceptionCast.cast(ContentSearchErrorCode.E_150001);
        }
        return pageVO;
    }


    /**
     * 将响应结果封装为pageVO
     */
    private PageVO<CoursePubIndexDTO> getResponseMsg(SearchResponse response) {
        SearchHits hits = response.getHits();

        //1.获取满足查询条件的数据总条数
        long total = hits.getTotalHits().value;

        //2.获得查询结果的数据集合
        SearchHit[] searchHits = hits.getHits();
        ArrayList<CoursePubIndexDTO> list = new ArrayList<>();

        //3.循环遍历响应结果,封装内容

        for (SearchHit searchHit : searchHits) {
            //3.1获取id
            String id = searchHit.getId();

            //3.2获取coursePubIndex
            String source = searchHit.getSourceAsString();
            CoursePubIndexDTO coursePubIndexDTO = JsonUtil.jsonToObject(source, CoursePubIndexDTO.class);
            coursePubIndexDTO.setIndexId(new Long(id));
            //3.3获得高亮内容

            Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
            //获取课程名称高亮
            HighlightField name = highlightFields.get("name");
            if (!ObjectUtils.isEmpty(name)) {
                Text[] nameFragments = name.getFragments();
                StringBuffer nameStringBuffer = new StringBuffer();
                for (Text nameFragment : nameFragments) {
                    nameStringBuffer.append(nameFragment);
                }
                coursePubIndexDTO.setName(nameStringBuffer.toString());
            }
            //获取描述高亮
            HighlightField description = highlightFields.get("description");
            if (!ObjectUtils.isEmpty(description)) {
                Text[] descriptionFragments = description.getFragments();
                StringBuffer descriptionStringBuffer = new StringBuffer();

                for (Text descriptionFragment : descriptionFragments) {
                    descriptionStringBuffer.append(descriptionFragment);
                }
                coursePubIndexDTO.setDescription(descriptionStringBuffer.toString());
            }

            list.add(coursePubIndexDTO);
        }
        //封装数据对象
        PageVO<CoursePubIndexDTO> pageVO = new PageVO<>();
        pageVO.setCounts(total);
        pageVO.setItems(list);
        return pageVO;
    }

    /**
     * 构建Request对象
     * Request --> SearchSourceBuilder ---->QueryBuilder
     * ---->分页
     * ---->高亮
     */
    private SearchRequest getRequest(PageRequestParams pageParams, QueryCoursePubModel queryModel) {


        //1.创建请求对象
        SearchRequest request = new SearchRequest(indexName);

        //2.构建查询的源数据对象
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        //2.1设置分页数据
        int pageNo = pageParams.getPageNo().intValue();
        int pageSize = pageParams.getPageSize();
        int from = (pageNo - 1) * pageSize;

        sourceBuilder.from(from);
        sourceBuilder.size(pageSize);
        //2.2设置高亮数据
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.preTags("<font color='red'><b>");
        highlightBuilder.postTags("</b></font>");
        highlightBuilder.fields().add(new HighlightBuilder.Field("name"));
        highlightBuilder.fields().add(new HighlightBuilder.Field("description"));

        sourceBuilder.highlighter(highlightBuilder);
        //3.构建查询条件
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        //用户输入的关键字(分词,多属性)
        String keyword = queryModel.getKeyword();
        if (StringUtil.isNotBlank(keyword)) {
            MultiMatchQueryBuilder matchQueryBuilder = QueryBuilders.multiMatchQuery(keyword, "name", "description");
            matchQueryBuilder.minimumShouldMatch("70%");
            matchQueryBuilder.field("name", 10f);
            boolQueryBuilder.must(matchQueryBuilder);
        }

        //课程分类(不需要分词)
        String mt = queryModel.getMt();
        String st = queryModel.getSt();
        if (StringUtil.isNotBlank(mt)) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("mt", mt));
        }
        if (StringUtil.isNotBlank(st)) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("st", st));
        }
        //课程等级(不需要分词)
        String grade = queryModel.getGrade();
        if (StringUtil.isNotBlank(grade)) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("grade", grade));
        }
        //设置查询方式
        sourceBuilder.query(boolQueryBuilder);
        //4.将查询源对象设置到请求对象中
        request.source(sourceBuilder);
        return request;
    }

    /**
     * 根据id查询课程发布数据
     *
     * @param coursePubId id
     * @return 课程发布数据
     * ===业务分析===
     * 1.判断关键数据
     * 2.根据id从索引库中获取课程发布数据
     * 3.将响应结果封装并返回
     */
    @Override
    public CoursePubIndexDTO getCoursePubIndexById(Long coursePubId) {
        CoursePubIndexDTO dto = null;
        try {
            //1.判断关键数据
            if (ObjectUtils.isEmpty(coursePubId)) {
                ExceptionCast.cast(CommonErrorCode.E_100101);
            }

            //2.根据id从索引库中获取课程发布数据
            GetRequest getRequest = new GetRequest(indexName, coursePubId.toString());
            GetResponse response = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
            String jsonString = response.getSourceAsString();

            //3.将响应结果封装并返回
            if (StringUtil.isNotBlank(jsonString)) {
                dto = JsonUtil.jsonToObject(jsonString, CoursePubIndexDTO.class);
                String id = response.getId();
                dto.setIndexId(new Long(id));
            }
        } catch (IOException e) {
            log.error("搜索课程失败,错误信息为:{}", e.getMessage());
        }
        return dto;
    }


    /**
     * 根据课程发布Id查询课程发布索引数据
     *
     * @param coursePubId 课程发布信息id
     * @return 课程发布信息
     * ===业务分析===
     * 1.判断关键数据
     * 2.根据id从索引库中获取课程发布数据
     * 3.将响应结果封装并返回
     */
    @Override
    public RestResponse<CoursePubIndexDTO> getCoursePubIndexById4s(Long coursePubId) {

        CoursePubIndexDTO dto = null;
        try {
            dto = this.getCoursePubIndexById(coursePubId);
        } catch (Exception e) {
            return RestResponse.validfail(ContentSearchErrorCode.E_150001);
        }
        return RestResponse.success(dto);
    }
}